﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ZeroBugBounce.Missive
{
	public partial class Messenger
	{
		public Receipt Send<T>(Boolean cancelable = true) where T : new()
		{
			return Send(new T(), cancelable);
		}

		public Receipt Send<T>(T message, Boolean cancelable = true)
		{
			return Send<T>(Topic.DefaultTopicText, message, cancelable);
		}

		public Receipt Send<T, TReply>(T message, Action<TReply> receiveReply, Boolean cancelable = true)
		{
			return Send<T, TReply>(Topic.DefaultTopicText, message, receiveReply, cancelable);
		}

		public Receipt Send<T>(object topic, T message, Boolean cancelable = true)
		{
			var realTopic = ParseTopic(topic);

			var key = new Tuple<Topic, Type>(realTopic, typeof(T));
			HandlerLock.Enter();
			Handler<T>[] handlers = CachedHandlers[key] as Handler<T>[];
			HandlerLock.Leave();

			Receipt receipt = default(Receipt);

			HandlerContext<T> handlerContext;
			CancellationTokenSource cancellationSource = null;

			receipt = default(Receipt);
			handlerContext = new HandlerContext<T>
			{
				Message = message,
				Topic = realTopic,
				CancellationToken = default(CancellationToken)
			};

			if (hasMessageSentHandlers)
				RaiseMessageSend(realTopic, message, false);

			if (cancelable)
			{
				for (int handlerIndex = 0; handlerIndex < handlers.Length; handlerIndex++)
				{
					Handler<T> handler = handlers[handlerIndex];
					if (handler.Cancelable && cancellationSource == null)
					{
						cancellationSource = new CancellationTokenSource();
						handlerContext.CancellationToken = cancellationSource.Token;
						receipt = new Receipt(cancellationSource);
					}

					Dispatcher.Enqueue(new DispatchContext<T>(this, handler, handlerContext));
				}
			}
			else
			{
				for (int handlerIndex = 0; handlerIndex < handlers.Length; handlerIndex++)
				{
					Handler<T> handler = handlers[handlerIndex];
					Dispatcher.Enqueue(new DispatchContext<T>(this, handler, handlerContext));
				}
			}

			return receipt;
		}

		public Receipt Send<T, TReply>(object topic, T message, Action<TReply> receiveReply, Boolean cancelable = true)
		{
			var realTopic = ParseTopic(topic);
			Receipt receipt = default(Receipt);
			ReplyHandlerContext<T, TReply> handlerContext;
			CancellationTokenSource cancellationSource = null;

			handlerContext = new ReplyHandlerContext<T, TReply>
			{
				Message = message,
				Topic = realTopic,
				ReplyCallback = receiveReply,
				CancellationToken = default(CancellationToken),
				CanReply = true
			};

			if (hasMessageSentHandlers)
				RaiseMessageSend(realTopic, message, true);

			var key = new Tuple<Topic, Type>(realTopic, typeof(T));
			HandlerLock.Enter();
			Handler<T>[] handlers = CachedHandlers[key] as Handler<T>[];
			HandlerLock.Leave();

			if (cancelable)
			{
				for(int handlerIndex = 0; handlerIndex < handlers.Length; handlerIndex++)				
				{
					Handler<T> handler = handlers[handlerIndex];

					if (handler.Cancelable && cancellationSource == null)
					{
						cancellationSource = new CancellationTokenSource();
						handlerContext.CancellationToken = cancellationSource.Token;
						receipt = new Receipt(cancellationSource);
					}

					Dispatcher.Enqueue(new DispatchContext<T>(this, handler, handlerContext));
				}
			}
			else
			{
				for (int handlerIndex = 0; handlerIndex < handlers.Length; handlerIndex++)
				{
					Handler<T> handler = handlers[handlerIndex];
					Dispatcher.Enqueue(new DispatchContext<T>(this, handler, handlerContext));
				}
			}

			return receipt;
		}
	}
}
